///-------------------------------------------------------------------------------------------------
// file:	ObservableEvents\ObservableEvent.h
//
// summary:	Declares the observable event class
//			TODO: Finish this
///-------------------------------------------------------------------------------------------------

#pragma once 

#include <string>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
#include <sstream>

#include "../VectorTimer/VectorTimer.h"
#include "../VectorTimer/FixedVectorTimer.h"
#include "../UtilityClasses/HelperClass.h"

//#include "../../RayTracing/Integrators/RadianceDetails/RadiancePoint.h"



using std::string;
using namespace boost::uuids;

namespace Network
{
	namespace ObservableEvents
	{

		template <typename T, typename S>
		class ObservableEventsList;

		

		///-------------------------------------------------------------------------------------------------
		/// <summary>	Values that represent observable types. </summary>
		///
		/// <remarks>	Adrian, 05/09/2015. </remarks>
		enum OBSERVABLE_PRODUCED_TYPE
		{
			INTERNAL_UPDATE,
			EXTERNAL_UPDATE
		};

		///-------------------------------------------------------------------------------------------------
		/// <summary>	An observable event. </summary>
		///
		/// <remarks>	Adrian, 05/09/2015. </remarks>
		///-------------------------------------------------------------------------------------------------
		template <typename T, typename IC>
		class ObservableEvent
		{

		public:
			friend class ObservableEventsList<T, IC>;

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Adrian, 23/09/2015. </remarks>
			///
			/// <param name="p_type">	The type. </param>
			///-------------------------------------------------------------------------------------------------
			//by defualt insert 
			ObservableEvent()
			{
				//m_uuid = GetNextUuid();
				m_timeStamp = new FixedVectorTimer();
				m_uuid = GetNextUuid();
				m_type = 0;
				m_committed = false;
			}



			ObservableEvent(int p_type)
			{
				//m_uuid = GetNextUuid();
				m_timeStamp = new FixedVectorTimer();
				m_uuid = GetNextUuid();
				m_type = p_type;
				m_committed = false;
			}


			ObservableEvent(int p_type, string p_vectorTimer)
			{
				//m_uuid = GetNextUuid();
				m_timeStamp = new FixedVectorTimer(p_vectorTimer);
				m_uuid = GetNextUuid();
				m_type = p_type;
				m_committed = false;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <param name="p_currentTime">	  	[in,out] If non-null, the current time. </param>
			/// <param name="p_updateRepresented">	The update represented. </param>
			/// <param name="p_type">			  	The type. </param>
			///-------------------------------------------------------------------------------------------------

			ObservableEvent(VectorTimer* p_currentTime, T p_updateRepresented, int p_epoch, int p_updateType, OBSERVABLE_PRODUCED_TYPE p_provenance = INTERNAL_UPDATE)
			{
				m_uuid = GetNextUuid();
				m_timeStamp = new FixedVectorTimer(*((FixedVectorTimer*)p_currentTime));
				T* temp = new T(p_updateRepresented);
				m_updateRepresented.push_back(temp);
				m_type = p_updateType;

				m_prodType = p_provenance;
				
				if (p_provenance == EXTERNAL_UPDATE)
					m_highEpochNumber = p_epoch;
				else
					m_lowEpochNumber = p_epoch;

				
				m_committed = false;
			}


			///-------------------------------------------------------------------------------------------------
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Adrian, 23/09/2015. </remarks>
			///
			/// <param name="p_currentTime">	  	[in,out] If non-null, the current time. </param>
			/// <param name="p_updateRepresented">	[in,out] If non-null, the update represented. </param>
			/// <param name="p_epoch">			  	The epoch. </param>
			/// <param name="p_updateType">		  	Type of the update. </param>
			/// <param name="p_provenance">		  	The provenance. </param>
			///-------------------------------------------------------------------------------------------------

			ObservableEvent(vector<T*> p_updateRepresented, int p_updateType, OBSERVABLE_PRODUCED_TYPE p_provenance = INTERNAL_UPDATE)
			{
				m_uuid = GetNextUuid();
				m_updateRepresented = p_updateRepresented;
				m_type = p_updateType;


				m_prodType = p_provenance;
			}

			ObservableEvent(string inputstring, int p_epoch, OBSERVABLE_PRODUCED_TYPE p_provenance = INTERNAL_UPDATE)
			{
				//FromString(inputstring);
				m_uuid = GetNextUuid();
				m_prodType = p_provenance;
				SetEpoch(p_epoch);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Convert this object into a string representation. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <returns>	A string that represents this object. </returns>
			///-------------------------------------------------------------------------------------------------


			virtual void Commit(IC*) = 0;

			virtual string ToString() = 0;
			/*{
				std::stringstream ss;
				ss << m_type;
				ss << "_";
				ss << GetStringUpdate();
				ss << "_";
				ss << m_uuid;
				ss << "_";
				ss << m_timeStamp->ToString();
				return ss.str();
			}*/


			void ParseUpdate(string p_inputupdate)
			{
				//test puproses only for now to see that everything is working ok
				T* temp = new T(p_inputupdate);
				m_updateRepresented.push_back(temp);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Initializes this object from the given from string. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <param name="p_inputString">	The input string. </param>
			///-------------------------------------------------------------------------------------------------

			virtual void FromString(string p_inputString) = 0;
			virtual void ToSerialize(std::vector<unsigned char>&) = 0;
			virtual void FromSerialize(vector<unsigned char>&, int) = 0;
			
			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets string update. </summary>
			///
			/// <remarks>	Adrian, 23/09/2015. </remarks>
			///
			/// <returns>	The string update. </returns>
			///-------------------------------------------------------------------------------------------------

			virtual string GetStringUpdate() = 0;
			
			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets time stamps. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <returns>	null if it fails, else the time stamps. </returns>
			///-------------------------------------------------------------------------------------------------

			VectorTimer* GetTimeStamps()
			{
				return m_timeStamp;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets observable update. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <returns>	The observable update. </returns>
			///-------------------------------------------------------------------------------------------------

			T GetObservableUpdate()
			{
				return m_updateRepresented;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Equal observable events </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <param name="p_comparer">	The comparer. </param>
			///
			/// <returns>	true if the parameters are considered equivalent. </returns>
			///-------------------------------------------------------------------------------------------------

			T operator = (const ObservableEvent<T, IC>& p_comparer)
			{
				m_uuid = p_comparer.GetUuid();
				m_timeStamp = p_comparer.GetTimeStamps();
				m_prodType = p_comparer.m_prodType();
				return *this;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets event type. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <returns>	The event type. </returns>
			///-------------------------------------------------------------------------------------------------

			int GetType()
			{
				return m_type;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets the uuid. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <returns>	The uuid. </returns>
			///-------------------------------------------------------------------------------------------------

			uuid GetUuid()
			{
				return m_uuid;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sets time stamp. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <param name="p_timeStamp">	[in,out] If non-null, the time stamp. </param>
			///-------------------------------------------------------------------------------------------------

			void SetTimeStamp(VectorTimer* p_timeStamp)
			{
				m_timeStamp = new FixedVectorTimer(*((FixedVectorTimer*)p_timeStamp));
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Sets represented update. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <param name="p_update">	The update. </param>
			///-------------------------------------------------------------------------------------------------

			void SetRepresentedUpdate(T p_update)
			{
				m_updateRepresented = p_update;
			}

			bool operator == (const ObservableEvent<T, IC>& p_currentEvent)
			{
				return m_uuid == p_currentEvent.m_uuid;
			}

			void SetEpoch(int p_epoch)
			{
				switch (m_prodType)
				{
				case INTERNAL_UPDATE:
					m_lowEpochNumber = p_epoch;
					break;
				case EXTERNAL_UPDATE:
					m_highEpochNumber = p_epoch;
					break;
				default:
					m_lowEpochNumber = p_epoch;
					break;
				}
			}

			void SetCommitted(bool p_value)
			{
				m_committed = p_value;
			}

			bool GetCommitted()
			{
				return m_committed;
			}

			bool operator < (const ObservableEvent<T, IC>& p_other)
			{
				return preFunction(p_other);
			}

			void DamagedObservableEvent()
			{
				m_parskedOk = false;
			}

			bool GetObservableEventStatus()
			{
				return m_parskedOk;
			}

			virtual ~ObservableEvent()
			{}

			void InsertPoint(T* p_currentPoint)
			{
				m_updateRepresented.push_back(p_currentPoint);
			}


		protected:
			/// <summary>	The uuid. </summary>
			boost::uuids::uuid m_uuid;
			/// <summary>	The type. </summary>
			int m_type;
			/// <summary>	How was observable event produced. </summary>
			OBSERVABLE_PRODUCED_TYPE m_prodType;
			/// <summary>	The time stamp. </summary>
			VectorTimer* m_timeStamp;
			/// <summary>	The update represented. </summary>
			vector<T*> m_updateRepresented;
			/// <summary>	The high epoch number. </summary>
			int m_highEpochNumber;
			/// <summary>	The low epoch number. </summary>
			int m_lowEpochNumber;
			
			bool m_committed;

			bool m_parskedOk = true;



			///-------------------------------------------------------------------------------------------------
			/// <summary>	Gets the next uuid. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <returns>	The next uuid. </returns>
			///-------------------------------------------------------------------------------------------------

			uuid GetNextUuid()
			{
				random_generator gen;
				uuid id = gen();

				return id;
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Pre function. </summary>
			///
			/// <remarks>	Adrian, 11/09/2015. </remarks>
			///
			/// <param name="p_rhs">	[in,out] If non-null, the right hand side. </param>
			///
			/// <returns>	true if it succeeds, false if it fails. </returns>
			///-------------------------------------------------------------------------------------------------

			static bool preFunction( ObservableEvent<T, IC>& p_lhs,  ObservableEvent <T, IC>& p_rhs)
			{
				if (p_lhs.GetUuid() == p_rhs.GetUuid())
				{
					return false;
				}
				else if (p_lhs.m_timeStamp == p_rhs.m_timeStamp)
				{
					return TieFunction(p_lhs,p_rhs);
				}
				else if (p_lhs.m_timeStamp <= p_rhs->GetTimeStamps())
				{
					return true;
				}
				else if (p_rhs->GetTimeStamps() <= p_lhs.m_timeStamp)
				{
					return false;
				}
				else
					return TieFunction(p_lhs, p_rhs);
			}

			///-------------------------------------------------------------------------------------------------
			/// <summary>	Tie function. </summary>
			///
			/// <remarks>	Adrian, 23/09/2015. </remarks>
			///
			/// <param name="p_rhs">	[in,out] If non-null, the right hand side. </param>
			///
			/// <returns>	true if it succeeds, false if it fails. </returns>
			///-------------------------------------------------------------------------------------------------

			bool TieFunction(ObservableEvent < T, IC> * p_rhs)
			{
				return (m_uuid < p_rhs->GetUuid());
			}

			bool TieFunction(ObservableEvent<T, IC>& p_lhs ,ObservableEvent < T, IC>& p_rhs)
			{
				return (p_lhs->GetUuid() < p_rhs->GetUuid());
			}

			inline bool operator == (ObservableEvent<T, IC>& p_rhs)
			{
				return m_uuid == p_rhs.GetUuid();
			}

		};

		template<typename T, typename IC>
		inline bool operator == (const ObservableEvent<T, IC>& lhs, const ObservableEvent<T, IC>& rhs)
		{
			return lhs.GetUuid() == rhs.GetUuid();
		};
	}
}